home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 July: Mac OS SDK / Dev.CD Jul 97 SDK1.toast / Development Kits (Disc 1) / QuickDraw 3D / Samples / SampleCode / BackGround / BackGroundSupport.c < prev    next >
Encoding:
Text File  |  1996-05-21  |  19.0 KB  |  715 lines  |  [TEXT/MPCC]

  1. // Quickdraw 3D sample code
  2. //
  3. // This file illustrates how to set up a pixmap based draw context.
  4. // A metafile is read into and imaged in the pixmap, this pixmap is combined
  5. // with a pixmap containing a background, so that the 3d data is drawn over 
  6. // the background
  7. //
  8. // Nick Thompson, AppleLink: DEVSUPPORT (devsupport@applelink.apple.com)
  9. //
  10. // ©1994-5 Apple Computer Inc., All Rights Reserved
  11.  
  12.  
  13.  
  14. #include <Files.h>
  15. #include <QuickDraw.h>
  16. #include <QDOffScreen.h>
  17. #include <StandardFile.h>
  18.  
  19. #include "BackGroundSupport.h"
  20.  
  21. #include "QD3D.h"
  22. #include "QD3DDrawContext.h"
  23. #include "QD3DRenderer.h"
  24. #include "QD3DShader.h"
  25. #include "QD3DCamera.h"
  26. #include "QD3DLight.h"
  27. #include "QD3DGeometry.h"
  28. #include "QD3DGroup.h"
  29. #include "QD3DMath.h"
  30. #include "QD3DTransform.h"
  31. #include "QD3DStorage.h"
  32. #include "QD3DIO.h"
  33.  
  34.  
  35. //-----------------------------------------------------------------------------------------------
  36. // local utility functions
  37. static    TQ3FileObject         MyGetNewFile( FSSpec *myFSSpec, TQ3Boolean *isText ) ;
  38.  
  39. static void GetGroupBBox(
  40.     DocumentPtr            theDocument,
  41.     TQ3BoundingBox         *viewBBox) ;
  42.                                                 
  43. static    TQ3Status MyAddShaderToGroup( TQ3GroupObject group ) ;
  44.  
  45. static TQ3Status GetDocumentGroupBoundingBox( 
  46.     DocumentPtr theDocument , 
  47.     TQ3BoundingBox *viewBBox) ;
  48.  
  49. //-----------------------------------------------------------------------------------------------
  50. // Submit the scene for rendering/fileIO and picking
  51. TQ3Status SubmitScene( DocumentPtr theDocument ) 
  52. {        
  53.     TQ3Vector3D                globalScale;
  54.     TQ3Vector3D                globalTranslate;
  55.     
  56.     globalScale.x = globalScale.y = globalScale.z = theDocument->fGroupScale;
  57.     globalTranslate = *(TQ3Vector3D *)&theDocument->fGroupCenter;
  58.     Q3Vector3D_Scale(&globalTranslate, -1, &globalTranslate);
  59.     Q3Style_Submit(theDocument->fInterpolation, theDocument->fView);
  60.     Q3Style_Submit(theDocument->fBackFacing , theDocument->fView);
  61.     Q3Style_Submit(theDocument->fFillStyle, theDocument->fView);
  62.         
  63.     Q3MatrixTransform_Submit( &theDocument->fRotation, theDocument->fView);
  64.         
  65.     Q3ScaleTransform_Submit(&globalScale, theDocument->fView);
  66.     Q3TranslateTransform_Submit(&globalTranslate, theDocument->fView);
  67.     Q3DisplayGroup_Submit( theDocument->fModel, theDocument->fView);
  68.     
  69.     return kQ3Success ;
  70. }
  71.  
  72. //-----------------------------------------------------------------------------------------------
  73.  
  74. static TQ3Status GetDocumentGroupBoundingBox( 
  75.     DocumentPtr theDocument , 
  76.     TQ3BoundingBox *viewBBox)
  77. {
  78.     TQ3Status        status;
  79.     TQ3ViewStatus    viewStatus ;
  80.     
  81.     status = Q3View_StartBoundingBox( theDocument->fView, kQ3ComputeBoundsApproximate );
  82.     do {
  83.         status = SubmitScene( theDocument ) ;
  84.     } while((viewStatus = Q3View_EndBoundingBox( theDocument->fView, viewBBox )) == kQ3ViewStatusRetraverse );
  85.     return status ;
  86. }
  87. //-----------------------------------------------------------------------------------------------
  88.  
  89. TQ3ViewObject MyNewView(GWorldPtr myOffscreenGWorld)
  90. {
  91.     TQ3Status                myStatus;
  92.     TQ3ViewObject            myView;
  93.     TQ3DrawContextObject        myDrawContext;
  94.     TQ3RendererObject        myRenderer;
  95.     TQ3CameraObject            myCamera;
  96.     TQ3GroupObject            myLights;
  97.     
  98.     if((myView = Q3View_New()) == NULL)
  99.         goto bail ;    
  100.         
  101.     //    Create and set draw context.
  102.     if ((myDrawContext = MyNewDrawContext(myOffscreenGWorld)) == NULL )
  103.         goto bail;
  104.         
  105.     if ((myStatus = Q3View_SetDrawContext(myView, myDrawContext)) == kQ3Failure )
  106.         goto bail;
  107.  
  108.     Q3Object_Dispose( myDrawContext ) ;
  109.     
  110.     //    Create and set renderer.
  111.     // this would use the interactive software renderer
  112.  
  113.     if ((myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeInteractive)) != NULL ) {
  114.         if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) {
  115.             goto bail;
  116.         }
  117.     }
  118.     else {
  119.         goto bail;
  120.     }
  121.  
  122.     Q3Object_Dispose( myRenderer ) ;
  123.     
  124.     //    Create and set camera.
  125.     if ( (myCamera = MyNewCamera(myOffscreenGWorld)) == NULL )
  126.         goto bail;
  127.         
  128.     if ((myStatus = Q3View_SetCamera(myView, myCamera)) == kQ3Failure )
  129.         goto bail;
  130.  
  131.     Q3Object_Dispose( myCamera ) ;
  132.     
  133.     //    Create and set lights.
  134.     if ((myLights = MyNewLights()) == NULL )
  135.         goto bail;
  136.         
  137.     if ((myStatus = Q3View_SetLightGroup(myView, myLights)) == kQ3Failure )
  138.         goto bail;
  139.         
  140.     Q3Object_Dispose(myLights);
  141.  
  142.     //    Done!!!
  143.     return ( myView );
  144.     
  145. bail:
  146.     //    If any of the above failed, then don't return a view.
  147.     SysBeep(10) ;
  148.     return ( NULL );
  149. }
  150.  
  151. //----------------------------------------------------------------------------------
  152.  
  153. TQ3DrawContextObject MyNewDrawContext(GWorldPtr theGWorld)
  154. {
  155.     TQ3PixmapDrawContextData    myDrawContextData;
  156.     TQ3ColorARGB                clearColor = {1.0, 0.0, 0.0, 0.0} ;
  157.     PixMapHandle                 hPixMap ;
  158.     Rect                        srcRect ;
  159.  
  160.     float                        factor = 0xffff ;
  161.  
  162.     clearColor.r = kClearColor.red / factor ;
  163.     clearColor.g = kClearColor.green / factor ;
  164.     clearColor.b = kClearColor.blue / factor ;
  165.     
  166.     //    Fill in draw context data.
  167.     myDrawContextData.drawContextData.clearImageMethod = kQ3ClearMethodWithColor;
  168.     myDrawContextData.drawContextData.clearImageColor  = clearColor;
  169.  
  170.     myDrawContextData.drawContextData.paneState = kQ3False;
  171.     myDrawContextData.drawContextData.maskState = kQ3False;
  172.     
  173.     myDrawContextData.drawContextData.doubleBufferState = kQ3False;
  174.  
  175.     hPixMap = GetGWorldPixMap(theGWorld);
  176.     LockPixels(hPixMap);
  177.  
  178.     srcRect = theGWorld->portRect;
  179.  
  180.     myDrawContextData.pixmap.width = srcRect.right  - srcRect.left;
  181.     myDrawContextData.pixmap.height= srcRect.bottom - srcRect.top;
  182.     
  183.     myDrawContextData.pixmap.rowBytes = (**hPixMap).rowBytes & 0x7FFF;
  184.     myDrawContextData.pixmap.pixelType = kQ3PixelTypeRGB32;
  185.     myDrawContextData.pixmap.pixelSize = 32;
  186.  
  187.     myDrawContextData.pixmap.bitOrder  = kQ3EndianBig;
  188.     myDrawContextData.pixmap.byteOrder = kQ3EndianBig;
  189.     
  190.     myDrawContextData.pixmap.image        = GetPixBaseAddr( hPixMap );
  191.     
  192.     return Q3PixmapDrawContext_New(&myDrawContextData);
  193. }
  194. //----------------------------------------------------------------------------------
  195.  
  196. TQ3CameraObject MyNewCamera(CGrafPtr thePort)
  197. {
  198.     TQ3CameraObject                    myCamera;
  199.     TQ3CameraData                    myCameraData;
  200.     TQ3ViewAngleAspectCameraData        myViewAngleCameraData;
  201.     TQ3Point3D                        cameraFrom     = { 0.0, 0.0, 30.0 };
  202.     TQ3Point3D                        cameraTo     = { 0.0, 0.0, 0.0 };
  203.     TQ3Vector3D                        cameraUp     = { 0.0, 1.0, 0.0 };
  204.     
  205.     float                             fieldOfView = .52359333333;
  206.     float                             hither         = 0.001;
  207.     float                             yon         = 1000;
  208.     
  209.     //    Fill in camera data.
  210.     myCameraData.placement.cameraLocation = cameraFrom;
  211.     myCameraData.placement.pointOfInterest = cameraTo;
  212.     myCameraData.placement.upVector = cameraUp;
  213.     
  214.     myCameraData.range.hither = hither;
  215.     myCameraData.range.yon = yon;
  216.     
  217.     myCameraData.viewPort.origin.x = -1.0;
  218.     myCameraData.viewPort.origin.y = 1.0;
  219.     myCameraData.viewPort.width = 2.0;
  220.     myCameraData.viewPort.height = 2.0;
  221.     
  222.     myViewAngleCameraData.cameraData = myCameraData;
  223.     myViewAngleCameraData.fov = fieldOfView ;
  224.     
  225.     // set up the aspect ratio based on the window
  226.     myViewAngleCameraData.aspectRatioXToY =  
  227.             (float) (thePort->portRect.right - thePort->portRect.left) / 
  228.             (float) (thePort->portRect.bottom - thePort->portRect.top);
  229.  
  230.     myCamera = Q3ViewAngleAspectCamera_New(&myViewAngleCameraData);    
  231.     
  232.     //    Return the camera.
  233.     return ( myCamera );
  234. }
  235.  
  236.  
  237. //----------------------------------------------------------------------------------
  238.  
  239. TQ3GroupObject MyNewLights()
  240. {
  241.     TQ3GroupPosition            myGroupPosition;
  242.     TQ3GroupObject            myLightList;
  243.     TQ3LightData                myLightData;
  244.     TQ3PointLightData        myPointLightData;
  245.     TQ3DirectionalLightData    myDirectionalLightData;
  246.     TQ3LightObject            myAmbientLight, myPointLight, myFillLight;
  247.     TQ3Point3D                pointLocation = { -10.0, 0.0, 10.0 };
  248.     TQ3Vector3D                fillDirection = { 10.0, 0.0, 10.0 };
  249.     TQ3ColorRGB                WhiteLight = { 1.0, 1.0, 1.0 };
  250.     
  251.     //    Set up light data for ambient light.  This light data will be used for point and fill
  252.     //    light also.
  253.  
  254.     myLightData.isOn = kQ3True;
  255.     myLightData.color = WhiteLight;
  256.     
  257.     //    Create ambient light.
  258.     myLightData.brightness = .2;
  259.     myAmbientLight = Q3AmbientLight_New(&myLightData);
  260.     if ( myAmbientLight == NULL )
  261.         goto bail;
  262.     
  263.     //    Create point light.
  264.     myLightData.brightness = 1.0;
  265.     myPointLightData.lightData = myLightData;
  266.     myPointLightData.castsShadows = kQ3False;
  267.     myPointLightData.attenuation = kQ3AttenuationTypeNone;
  268.     myPointLightData.location = pointLocation;
  269.     myPointLight = Q3PointLight_New(&myPointLightData);
  270.     if ( myPointLight == NULL )
  271.         goto bail;
  272.  
  273.     //    Create fill light.
  274.     myLightData.brightness = .2;
  275.     myDirectionalLightData.lightData = myLightData;
  276.     myDirectionalLightData.castsShadows = kQ3False;
  277.     myDirectionalLightData.direction = fillDirection;
  278.     myFillLight = Q3DirectionalLight_New(&myDirectionalLightData);
  279.     if ( myFillLight == NULL )
  280.         goto bail;
  281.  
  282.     //    Create light group and add each of the lights into the group.
  283.     myLightList = Q3LightGroup_New();
  284.     if ( myLightList == NULL )
  285.         goto bail;
  286.     myGroupPosition = Q3Group_AddObject(myLightList, myAmbientLight);
  287.     if ( myGroupPosition == 0 )
  288.         goto bail;
  289.     myGroupPosition = Q3Group_AddObject(myLightList, myPointLight);
  290.     if ( myGroupPosition == 0 )
  291.         goto bail;
  292.     myGroupPosition = Q3Group_AddObject(myLightList, myFillLight);
  293.     if ( myGroupPosition == 0 )
  294.         goto bail;
  295.  
  296.     Q3Object_Dispose( myAmbientLight ) ;
  297.     Q3Object_Dispose( myPointLight ) ;
  298.     Q3Object_Dispose( myFillLight ) ;
  299.  
  300.     //    Done!
  301.     return ( myLightList );
  302.     
  303. bail:
  304.     //    If any of the above failed, then return nothing!
  305.     return ( NULL );
  306. }
  307.  
  308. //----------------------------------------------------------------------------------
  309.  
  310. TQ3GroupObject MyNewModelFromFile(FSSpec *theFileSpec)
  311. {
  312.     TQ3GroupObject        myGroup = NULL ;
  313.     TQ3Boolean            isText = kQ3False ;
  314.     TQ3FileMode            myFileMode ;    // we are reading the file
  315.     TQ3FileObject        theFile = kQ3FileModeNormal;
  316.     
  317.     //    Create a ordered group for the complete model.
  318.     if ((myGroup = Q3OrderedDisplayGroup_New()) == NULL )
  319.         return NULL;
  320.  
  321.     theFile = MyGetNewFile( theFileSpec, &isText ) ;
  322.     
  323.     if( isText == kQ3True )
  324.         myFileMode |= kQ3FileModeText;    // is it a text metafile??    
  325.  
  326.     // Open the file object
  327.     if( Q3File_OpenRead( theFile, &myFileMode ) != kQ3Success)
  328.         return  NULL ;
  329.  
  330.     MyReadModelFromFile( theFile, myGroup ) ;
  331.     
  332.     Q3File_Close(theFile);            // close and dispose of the file object
  333.     Q3Object_Dispose(theFile);
  334.     
  335.     MyAddShaderToGroup( myGroup ) ;
  336.     
  337.     return myGroup ;
  338. }
  339.  
  340.  
  341. //----------------------------------------------------------------------------------
  342. // attach a shader to the group
  343.  
  344. TQ3Status MyAddShaderToGroup( TQ3GroupObject group )
  345. {
  346.     TQ3ShaderObject    illuminationShader = Q3PhongIllumination_New();
  347.  
  348.     Q3Group_AddObject(group, illuminationShader);
  349.     Q3Object_Dispose(illuminationShader);
  350.     return(kQ3Success);
  351. }
  352.  
  353. //----------------------------------------------------------------------------------
  354. // read model from file object into the supplied group
  355.  
  356. TQ3Status MyReadModelFromFile( TQ3FileObject theFile,TQ3GroupObject myGroup)
  357. {    
  358.     if(theFile != NULL) {
  359.     
  360.         TQ3Object            myTempObj ;
  361.         TQ3Boolean            isEOF ;
  362.                 
  363.     
  364.         // read objects from the file
  365.         do {
  366.         
  367.             myTempObj = Q3File_ReadObject( theFile );
  368.             
  369.             if( myTempObj != NULL ) {
  370.                 // we only want the object in our main group if we can draw it
  371.                 if (Q3Object_IsDrawable( myTempObj) ) 
  372.                     Q3Group_AddObject( myGroup, myTempObj ) ;
  373.                 
  374.                 // we either added the object to the main group, or we don't care
  375.                 // so we can safely dispose of the object
  376.                 Q3Object_Dispose( myTempObj ) ;
  377.             }
  378.             
  379.             // check to see if we reached the end of file yet
  380.             isEOF = Q3File_IsEndOfFile( theFile );
  381.             
  382.         } while (isEOF == kQ3False);    
  383.     }
  384.     
  385.     if( myGroup != NULL )
  386.         return kQ3Success ;
  387.     else
  388.         return kQ3Failure ;
  389. }
  390.  
  391. //-----------------------------------------------------------------------------------------------
  392. // cleaned up from IM QuickDraw 3D pp 15-5
  393. static TQ3FileObject MyGetNewFile( FSSpec *myFSSpec, TQ3Boolean *isText )
  394. {
  395.     TQ3FileObject        myFileObj;
  396.     TQ3StorageObject        myStorageObj;
  397.     OSType                myFileType;
  398.     
  399.     FInfo                fndrInfo ;
  400.  
  401.     // we assume the FSSpec passed in was valid, get the file information
  402.     // we need to know the file type, this routine may get called by an appleEvent
  403.     // handler, so we can't assume a type, we need to get it from the fsspec.
  404.     
  405.     FSpGetFInfo( myFSSpec, &fndrInfo ) ;
  406.     
  407.     // pull out the file type
  408.     
  409.     myFileType = fndrInfo.fdType ;
  410.     
  411.     // Create new storage object and new file object 
  412.     if(((myStorageObj = Q3FSSpecStorage_New( myFSSpec )) == NULL) 
  413.         || ((myFileObj = Q3File_New()) == NULL)) 
  414.     {
  415.         if (myStorageObj != NULL) 
  416.             Q3Object_Dispose(myStorageObj);
  417.         return(NULL);
  418.     }
  419.  
  420.     // Set the storage for the file object
  421.     Q3File_SetStorage(myFileObj, myStorageObj);
  422.     Q3Object_Dispose(myStorageObj);
  423.  
  424.     if (myFileType == '3DMF')
  425.         *isText = kQ3False ;
  426.     else if (myFileType == 'TEXT')
  427.         *isText = kQ3True ;
  428.  
  429.     return (myFileObj);
  430. }
  431.  
  432.  
  433. //-------------------------------------------------------------------------------------------
  434. //
  435. Boolean MetafileFileSpecify( FSSpec *theFile )
  436. {
  437.     StandardFileReply    theSFReply ;
  438.     SFTypeList            myTypes = { '3DMF' } ;
  439.     const short            numTypes = 1 ;
  440.         
  441.     // Get the file name to open
  442.     StandardGetFile( nil, numTypes, myTypes, &theSFReply ) ;
  443.     
  444.     if( theSFReply.sfGood )
  445.         *theFile = theSFReply.sfFile ;
  446.     
  447.     // did the user cancel?
  448.     return theSFReply.sfGood ;
  449.     
  450. }
  451. //-------------------------------------------------------------------------------------------
  452. //
  453. Boolean PictureFileSpecify( FSSpec *theFile )
  454. {
  455.     StandardFileReply    theSFReply ;
  456.     SFTypeList            myTypes = { 'PICT' } ;
  457.     const short            numTypes = 1 ;
  458.         
  459.     // Get the file name to open
  460.     StandardGetFile( nil, numTypes, myTypes, &theSFReply ) ;
  461.     
  462.     if( theSFReply.sfGood )
  463.         *theFile = theSFReply.sfFile ;
  464.     
  465.     // did the user cancel?
  466.     return theSFReply.sfGood ;
  467.     
  468. }
  469.  
  470. //----------------------------------------------------------------------------------
  471. PicHandle OpenPICTFile( FSSpec *theFile )
  472. {
  473.     OSErr        err;
  474.     long        curEOF;
  475.     PicHandle    my_pic;
  476.     long         count;
  477.     Ptr         buffer;
  478.     short        refNum;
  479.     
  480.     if (FSpOpenDF(theFile, fsRdWrPerm, &refNum))
  481.         return(0);
  482.  
  483.     /* get size of file */
  484.     err = GetEOF(refNum, &curEOF);
  485.     if (err != 0) {
  486.         return(0);
  487.     }
  488.     
  489.     /* move the file mark to 512 */
  490.     err = SetFPos(refNum, fsFromStart, 512L);
  491.     if (err != 0) {
  492.         return(0);
  493.     }
  494.  
  495.     /* size of data to read */
  496.     count = curEOF - 512;
  497.     
  498.     /* create the PicHandle */
  499.     my_pic = (PicHandle)NewHandle(count);
  500.     HLock((Handle)my_pic);
  501.     
  502.     /* read the PICT info */
  503.     buffer = (Ptr)(*my_pic);
  504.     err = FSRead(refNum, &count, buffer);
  505.     if (err != 0) {
  506.         return(0);
  507.     }
  508.     HUnlock((Handle)my_pic);
  509.  
  510.     FSClose(refNum);
  511.     
  512.     return (my_pic);
  513. }
  514. //----------------------------------------------------------------------------------
  515.  
  516.  
  517. void GetGroupBBox(
  518.     DocumentPtr            theDocument,
  519.     TQ3BoundingBox         *viewBBox)
  520. {
  521.     TQ3Point3D                     from     = { 0.0, 0.0, 1.0 };
  522.     TQ3Point3D                     to         = { 0.0, 0.0, 0.0 };
  523.     TQ3Vector3D                     up         = { 0.0, 1.0, 0.0 };
  524.     
  525.     float                         fieldOfView = .52359333333;
  526.     float                         hither         =  0.5;
  527.     float                         yon         =  1.5;
  528.     TQ3GroupObject                mainGroup = theDocument->fModel ;
  529.  
  530.     TQ3Status                    status;
  531.     
  532. #ifdef BETA_1_BUILD
  533.     Q3View_StartBounds( theDocument->fView );
  534.  
  535.     status = Q3DisplayGroup_BoundingBox(mainGroup, 
  536.                                         viewBBox, 
  537.                                         kQ3ComputeBoundsApproximate,
  538.                                          viewObject);
  539.  
  540.     Q3View_EndBounds( theDocument->fView );
  541. #else
  542.     status = GetDocumentGroupBoundingBox( theDocument , viewBBox) ;
  543. #endif
  544.                                         
  545.     //
  546.     //  If we have a point model, then the "viewBBox" would end up
  547.     //  being a "singularity" at the location of the point.  As
  548.     //  this bounding "box" is used in setting up the camera spec,
  549.     //  we get bogus input into Escher.
  550.     
  551.     {
  552.          float        xSize, ySize, zSize;
  553.         
  554.         xSize = viewBBox->max.x - viewBBox->min.x;
  555.         ySize = viewBBox->max.y - viewBBox->min.y;
  556.         zSize = viewBBox->max.z - viewBBox->min.z;
  557.  
  558.         if (xSize <= kQ3RealZero &&
  559.             ySize <= kQ3RealZero &&
  560.             zSize <= kQ3RealZero) {
  561.             
  562.             viewBBox->max.x += 0.0001;
  563.             viewBBox->max.y += 0.0001;
  564.             viewBBox->max.z += 0.0001;
  565.             
  566.             viewBBox->min.x -= 0.0001;
  567.             viewBBox->min.y -= 0.0001;
  568.             viewBBox->min.z -= 0.0001;
  569.         }
  570.     }
  571. }
  572.  
  573.  
  574.  
  575.  
  576. //------------------------------------------------------------------------
  577.  
  578.  
  579. TQ3Point3D AdjustCamera(
  580.     DocumentPtr            theDocument,
  581.     short                winWidth,
  582.     short                winHeight)
  583. {
  584.     float                         fieldOfView;
  585.     float                         hither;
  586.     float                         yon;
  587.     TQ3CameraPlacement            placement;
  588.     TQ3CameraRange                range;
  589.     TQ3BoundingBox                 viewBBox;
  590.     long                         fromAxis;    
  591.     float                         maxDimension;
  592.      float                        xSize, ySize, zSize;
  593.     float                        weights[2] = { 0.5, 0.5 };
  594.     TQ3Point3D                    points[2];
  595.     TQ3Vector3D                     viewVector;
  596.     TQ3Vector3D                    normViewVector;
  597.     TQ3Vector3D                    eyeToFrontClip;
  598.     TQ3Vector3D                    eyeToBackClip;
  599.     float                        viewDistance;
  600.     TQ3Vector3D                    diagonalVector;
  601.     float                        ratio;
  602.     TQ3CameraObject                camera;
  603.     
  604.     TQ3ViewObject                theView = theDocument->fView ;
  605.     TQ3GroupObject                mainGroup = theDocument->fModel ;
  606.     
  607.     TQ3Point3D                    *documentGroupCenter = &theDocument->fGroupCenter ;
  608.     float                        *documentGroupScale  = &theDocument->fGroupScale ;
  609.  
  610.     Q3View_GetCamera( theView, &camera);
  611.     GetGroupBBox( theDocument, &viewBBox);
  612.  
  613.     /*
  614.      *  If we have a point model, then the "viewBBox" would end up
  615.      *  being a "singularity" at the location of the point.  As
  616.      *  this bounding "box" is used in setting up the camera spec,
  617.      *  we get bogus input into Escher.
  618.      */
  619.     xSize = viewBBox.max.x - viewBBox.min.x;
  620.     ySize = viewBBox.max.y - viewBBox.min.y;
  621.     zSize = viewBBox.max.z - viewBBox.min.z;
  622.  
  623.     if (xSize <= kQ3RealZero &&
  624.         ySize <= kQ3RealZero &&
  625.         zSize <= kQ3RealZero)  {
  626.         viewBBox.max.x += 0.0001;
  627.         viewBBox.max.y += 0.0001;
  628.         viewBBox.max.z += 0.0001;
  629.         
  630.         viewBBox.min.x -= 0.0001;
  631.         viewBBox.min.y -= 0.0001;
  632.         viewBBox.min.z -= 0.0001;
  633.     }
  634.  
  635.     points[0] = viewBBox.min;
  636.     points[1] = viewBBox.max;
  637.  
  638.     Q3Point3D_AffineComb(points, weights, 2, documentGroupCenter);
  639.  
  640.     /*
  641.      *  The "from" point is on a vector perpendicular to the plane
  642.      *  in which the bounding box has greatest dimension.  As "up" is
  643.      *  always in the positive y direction, look at x and z directions.
  644.      */
  645.     xSize = viewBBox.max.x - viewBBox.min.x;
  646.     zSize = viewBBox.max.z - viewBBox.min.z;
  647.     
  648.     if (xSize > zSize) {
  649.         fromAxis = kQ3AxisZ;
  650.     } else {
  651.         fromAxis = kQ3AxisX;
  652.     }
  653.  
  654.     /*
  655.      *  Compute the length of the diagonal of the bounding box.
  656.      *
  657.      *  The hither and yon planes are adjusted so that the
  658.       *  diagonal of the bounding box is 7/8 the size of the
  659.       *  minimum dimension of the view frustum. The diagonal is used instead
  660.       *  of the maximum size (in x, y, or z) so that when you rotate
  661.       *  the object, the corners don't get clipped out.
  662.       */
  663.     Q3Point3D_Subtract(
  664.         &viewBBox.max,
  665.         &viewBBox.min,
  666.         &diagonalVector);
  667.  
  668.     maxDimension    =    Q3Vector3D_Length(&diagonalVector);
  669.     maxDimension    *=    8.0 / 7.0;
  670.     
  671.     ratio = 1.0 / maxDimension;
  672.             
  673.     *documentGroupScale = ratio;
  674.     
  675.     Q3Camera_GetPlacement(camera, &placement);
  676.  
  677.     Q3Point3D_Subtract(
  678.         &placement.cameraLocation,
  679.         &placement.pointOfInterest,
  680.         &viewVector);
  681.         
  682.     viewDistance = Q3Vector3D_Length(&viewVector);
  683.     
  684.     Q3Vector3D_Normalize(&viewVector, &normViewVector);
  685.     
  686.     Q3Vector3D_Scale(&normViewVector, 
  687.                      viewDistance - ratio * maxDimension/2.0,
  688.                      &eyeToFrontClip);
  689.                     
  690.     Q3Vector3D_Scale(&normViewVector, 
  691.                     viewDistance + ratio * maxDimension/2.0,
  692.                     &eyeToBackClip);
  693.  
  694.     hither     = Q3Vector3D_Length(&eyeToFrontClip);
  695.     yon     = Q3Vector3D_Length(&eyeToBackClip);
  696.     
  697.     fieldOfView = 2 * atan((ratio * maxDimension/2.0)/hither);
  698.  
  699.     range.hither                 = hither;
  700.     range.yon                     = yon;
  701.  
  702.     Q3Camera_SetRange(camera, &range);
  703.  
  704.     Q3ViewAngleAspectCamera_SetFOV(
  705.         camera, fieldOfView);
  706.  
  707.     Q3ViewAngleAspectCamera_SetAspectRatio(
  708.         camera, (float) winWidth / (float) winHeight);
  709.  
  710.     Q3Object_Dispose(camera);
  711.     
  712.     return( *documentGroupCenter );
  713. }
  714.  
  715.